package com.bw;

import com.alibaba.alink.operator.batch.BatchOperator;
import com.alibaba.alink.operator.batch.dataproc.SplitBatchOp;
import com.alibaba.alink.operator.batch.evaluation.EvalBinaryClassBatchOp;
import com.alibaba.alink.operator.batch.evaluation.EvalMultiClassBatchOp;
import com.alibaba.alink.operator.batch.feature.OneHotPredictBatchOp;
import com.alibaba.alink.operator.batch.feature.OneHotTrainBatchOp;
import com.alibaba.alink.operator.batch.source.CsvSourceBatchOp;
import com.alibaba.alink.operator.common.evaluation.BinaryClassMetrics;
import com.alibaba.alink.operator.common.evaluation.MultiClassMetrics;
import com.alibaba.alink.params.shared.clustering.HasKMeansDistanceType;
import com.alibaba.alink.pipeline.PipelineModel;
import com.alibaba.alink.pipeline.classification.DecisionTreeClassifier;
import com.alibaba.alink.pipeline.classification.KnnClassifier;
import com.alibaba.alink.pipeline.classification.LogisticRegression;
import com.alibaba.alink.pipeline.dataproc.vector.VectorAssembler;
import com.alibaba.alink.pipeline.tuning.*;

public class Test {
    public static void main(String[] args) throws Exception {
        // 设置并行度
        BatchOperator.setParallelism(1);

        String file = "datafile/kaggle-diabetes.txt";
        String schema = "f0 int,f1 int ,f2 int,f3 int,f4 int,f5 double,f6 double,age int,label int";
        CsvSourceBatchOp data1 = new CsvSourceBatchOp()
                .setFilePath(file)
                .setSchemaStr(schema)
                .setIgnoreFirstLine(true)
                .setFieldDelimiter(",");

//        data1.print();
        String[] features = {"f0","f1","f2","f3","f4","f5","f6","age"};
        //目标列 有糖尿病是1 没有糖尿病是0
        String label="label";
        System.out.println("特征列："+features.toString());
        System.out.println("目标列(有糖尿病是1 没有糖尿病是0)："+ label);

        // 特征转换
        BatchOperator <?> one_hot = new OneHotTrainBatchOp().setSelectedCols("age");
        BatchOperator <?> model = data1.link(one_hot);
        BatchOperator <?> predictor = new OneHotPredictBatchOp().setOutputCols("age_new");
        BatchOperator<?> result = predictor.linkFrom(model, data1);

        // 向量聚合
        VectorAssembler res = new VectorAssembler()
                .setSelectedCols("f0","f1","f2","f3","f4","f5","f6","age_new")
                .setOutputCol("vec");
        BatchOperator<?> data3 = res.transform(result);
        // 最终数据集
        data3.print();
        // 划分数据集
        BatchOperator<?> trainData = new SplitBatchOp().setFraction(0.8).linkFrom(data3);
        BatchOperator<?> testData = trainData.getSideOutput(0);


        // 逻辑回归
        LogisticRegression lr = new LogisticRegression()
                .setVectorCol("vec")
                .setLabelCol("label")
                .setL1(0.1)
                .setPredictionDetailCol("pred_detail")
                .setPredictionCol("pred");

        //knn
        KnnClassifier knn = new KnnClassifier()
                .setVectorCol("vec")
                .setPredictionCol("pred")
                .setPredictionDetailCol("pred_detail")
                .setLabelCol("label")
                .setK(3);


        //决策树 不能用向量
        DecisionTreeClassifier tree = new DecisionTreeClassifier()
                .setNumThreads(5)
                .setPredictionDetailCol("pred_detail")
                .setPredictionCol("pred")
                .setLabelCol("label")
                .setFeatureCols(features);

        // 结果
        BatchOperator<?> lr_result = lr.fit(trainData).transform(testData);
        BatchOperator<?> knn_result = knn.fit(trainData).transform(testData);
        BatchOperator<?> tree_result = tree.fit(trainData).transform(testData);
        lr_result.print();
        knn_result.print();
        tree_result.print();


        // 评估指标
        // 二分类
//        BinaryClassMetrics metrics = new EvalBinaryClassBatchOp().setLabelCol("label").setPredictionDetailCol(
//                "pred_detail").linkFrom(lr_result).collectMetrics();
//
//        System.out.println("metrics.getAccuracy() = " + metrics.getAccuracy());
//        System.out.println("metrics.getRecall() = " + metrics.getRecall());

        EvalMultiClassBatchOp evop1 = new EvalMultiClassBatchOp().setLabelCol("label").setPredictionDetailCol(
                "pred_detail");
        MultiClassMetrics lr_metrics = evop1.linkFrom(lr_result).collectMetrics();
        System.out.println("Prefix0 accuracy:" + lr_metrics.getAccuracy("1"));
        System.out.println("Micro Recall:" + lr_metrics.getMicroRecall());


        // 多分类 KNN只能用多分类
        EvalMultiClassBatchOp evop2 = new EvalMultiClassBatchOp().setLabelCol("label").setPredictionDetailCol(
                "pred_detail");
        MultiClassMetrics knn_metrics = evop2.linkFrom(knn_result).collectMetrics();
        System.out.println("Prefix0 accuracy:" + knn_metrics.getAccuracy("1"));
        System.out.println("Micro Recall:" + knn_metrics.getMicroRecall());


        EvalMultiClassBatchOp evop3 = new EvalMultiClassBatchOp().setLabelCol("label").setPredictionDetailCol(
                "pred_detail");
        MultiClassMetrics tree_metrics = evop3.linkFrom(tree_result).collectMetrics();
        System.out.println("Prefix0 accuracy:" + knn_metrics.getAccuracy("1"));
        System.out.println("Micro Recall:" + tree_metrics.getMicroRecall());


        // 调优
        //逻辑回归
        BinaryClassificationTuningEvaluator tuningEval1 = new BinaryClassificationTuningEvaluator() //用于在网格搜索中评估逻辑回归和决策树
                .setLabelCol("label")
                .setPredictionDetailCol("pred_detail")
                .setTuningBinaryClassMetric("ACCURACY");

        MultiClassClassificationTuningEvaluator tuningEval2 = new MultiClassClassificationTuningEvaluator()//用于在网格搜索中评估knn
                .setLabelCol("label")
                .setPredictionDetailCol("pred_detail")
                .setTuningMultiClassMetric("ACCURACY");

        // 对逻辑回归调优
        ParamGrid lr_paramGrid = new ParamGrid()
                .addGrid(lr, LogisticRegression.L_2, new Double[] {0.1,0.2})
                .addGrid(lr, LogisticRegression.MAX_ITER, new Integer[] {50, 120});
        GridSearchCV lr_cv = new GridSearchCV()
                .setEstimator(lr)
                .setParamGrid(lr_paramGrid)
                .setTuningEvaluator(tuningEval1)
                .setNumFolds(2)
                .enableLazyPrintTrainInfo("TrainInfo");
        GridSearchCVModel lr_model = lr_cv.fit(trainData);
        PipelineModel lr_best_model = lr_model.getBestPipelineModel();

        //knn
        ParamGrid knn_paramGrid = new ParamGrid()
                .addGrid(knn, KnnClassifier.K, new Integer[] {2,3})
                .addGrid(knn, KnnClassifier.DISTANCE_TYPE, new HasKMeansDistanceType.DistanceType[] {HasKMeansDistanceType.DistanceType.EUCLIDEAN, HasKMeansDistanceType.DistanceType.COSINE});
        GridSearchCV knn_cv = new GridSearchCV()
                .setEstimator(knn)
                .setParamGrid(knn_paramGrid)
                .setTuningEvaluator(tuningEval2)
                .setNumFolds(2)
                .enableLazyPrintTrainInfo("TrainInfo");
        GridSearchCVModel knn_model = knn_cv.fit(trainData);
        PipelineModel knn_best_model = knn_model.getBestPipelineModel();

        //决策树
        ParamGrid tree_paramGrid = new ParamGrid()
                .addGrid(tree, DecisionTreeClassifier.MIN_SAMPLES_PER_LEAF, new Integer[] {1,3})
                .addGrid(tree, DecisionTreeClassifier.MIN_INFO_GAIN, new Double[] {0.0,0.2});
        GridSearchCV tree_cv = new GridSearchCV()
                .setEstimator(tree)
                .setParamGrid(tree_paramGrid)
                .setTuningEvaluator(tuningEval2)
                .setNumFolds(2)
                .enableLazyPrintTrainInfo("TrainInfo");
        GridSearchCVModel tree_model = tree_cv.fit(trainData);
        PipelineModel tree_best_model = tree_model.getBestPipelineModel();

        // 7 使用最近模型训练
        BatchOperator<?> result1 = lr_best_model.transform(testData);
        BatchOperator<?> result2 = knn_best_model.transform(testData);
        BatchOperator<?> result3 = tree_best_model.transform(testData);
        // 收集指标
        MultiClassMetrics lr_best_metrics = evop1.linkFrom(result1).collectMetrics();
        MultiClassMetrics knn_best_metrics = evop2.linkFrom(result2).collectMetrics();
        MultiClassMetrics tree_best_metrics = evop3.linkFrom(result3).collectMetrics();
        System.out.println("lr_best_metrics.getAccuracy() = " + lr_best_metrics.getAccuracy());
        System.out.println("knn_best_metrics.getAccuracy() = " + knn_best_metrics.getAccuracy());
        System.out.println("tree_best_metrics.getAccuracy() = " + tree_best_metrics.getAccuracy());


        // 保存模型

        lr_best_model.save("datafile/yk5_1",true);
        knn_best_model.save("datafile/yk5_2",true);
        tree_best_model.save("datafile/yk5_3",true);

        BatchOperator.execute();
    }
}
